package hk.ust.comp3111.ustnutrition.formula;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

import hk.ust.comp3111.ustnutrition.MiscUtils;
import hk.ust.comp3111.ustnutrition.data.DataAccessor;
import hk.ust.comp3111.ustnutrition.data.DataTable;
import hk.ust.comp3111.ustnutrition.data.db.Food;
import hk.ust.comp3111.ustnutrition.data.db.User;

public class FoodSuggestion {
	public FoodSuggestion(User user) {
		mUser = user;
		cal = new NutritionCalculator(user);
	}

	User mUser;
	NutritionCalculator cal;

	public Food[] getRecommenedFoods() {
		cal.calc();
		double[] consumptions = mUser.getNutritions();
		final double CarbohydrateRatio = consumptions[1]
				/ cal.getCarbohydratesNeed();
		final double ProteinRatio = consumptions[2] / cal.getProteinNeed();
		final double FatRatio = consumptions[3] / cal.getFatNeed();
		final double SodiumRatio = consumptions[4] / cal.getSodiumNeed();

		int mode;
		double condition = 0;
		if (CarbohydrateRatio <= ProteinRatio && CarbohydrateRatio <= FatRatio
				&& CarbohydrateRatio <= SodiumRatio)
			if (CarbohydrateRatio < 0.3)
				mode = 1;
			else {
				mode = 2;
				condition = (1-CarbohydrateRatio)*0.7*consumptions[1];
			}
		else if (ProteinRatio <= CarbohydrateRatio && ProteinRatio <= FatRatio
				&& ProteinRatio <= SodiumRatio)
			if (ProteinRatio < 0.3)
				mode = 3;
			else {
				mode = 4;
				condition = (1-ProteinRatio)*0.7*consumptions[2];
			}
		else if (FatRatio <= CarbohydrateRatio && FatRatio <= ProteinRatio
				&& FatRatio <= SodiumRatio)
			if (FatRatio < 0.3)
				mode = 5;
			else {
				mode = 6;
				condition = (1-FatRatio)*0.7*consumptions[3];
			}
		else if (SodiumRatio <= CarbohydrateRatio
				&& SodiumRatio <= ProteinRatio && SodiumRatio <= FatRatio)
			if (SodiumRatio < 0.3)
				mode = 7;
			else {
				mode = 8;
				condition = (1-SodiumRatio)*0.7*consumptions[4];
			}
		else
			mode = 0;

		final int mmode = mode;
		final double mCondition = condition;
		final double maxCarbohydrate = Math.min(consumptions[1] / 2.5,
				cal.getCarbohydratesNeed() * 1.2 - consumptions[1]);
		final double maxProtein = Math.min(consumptions[2] / 2.5,
				cal.getProteinNeed() * 1.2 - consumptions[2]);
		final double maxFat = Math.min(consumptions[3] / 2.5,
				cal.getFatNeed() * 1.2 - consumptions[3]);
		final double maxSodium = Math.min(consumptions[4] / 2.5,
				cal.getSodiumNeed() * 1.2 - consumptions[4]);

		DataTable<Food> foodTable = DataAccessor.Instance.getFoods();
		List<Food> foods = MiscUtils.iteratorToList(foodTable.iterator());

		final Random rnd = new Random();
		Collections.sort(foods, new Comparator<Food>() {
			@Override
			public int compare(Food a, Food b) {
				boolean aGood = false, bGood = false;
				if (a.getCarbohydrate() <= maxCarbohydrate
						&& a.getProtein() <= maxProtein && a.getFat() <= maxFat
						&& a.getSodium() <= maxSodium)
					aGood = true;

				if (b.getCarbohydrate() <= maxCarbohydrate
						&& b.getProtein() <= maxProtein && b.getFat() <= maxFat
						&& b.getSodium() <= maxSodium)
					bGood = true;

				if (aGood != bGood)
					return aGood ? -1 : 1;
				
				double aVal, bVal;

				switch (mmode) {
				case 1:
				case 2:
					aVal = a.getCarbohydrate();
					bVal = b.getCarbohydrate();
					break;
				case 3:
				case 4:
					aVal = a.getProtein();
					bVal = b.getProtein();
					break;
				case 5:
				case 6:
					aVal = a.getFat();
					bVal = b.getFat();
					break;
				case 7:
				case 8:
					aVal = a.getSodium();
					bVal = b.getSodium();
					break;
				default:
					return rnd.nextInt(2) - 1;
				}
				
				if(mmode % 2 == 0) {
					aGood = false;
					bGood = false;
					if(aVal < mCondition)
						aGood = true;
					if(bVal < mCondition)
						bGood = true;
					
					if (aGood != bGood)
						return aGood ? -1 : 1;
				}
				
				return Double.compare(bVal, aVal);
			}
		});
		
		foods = foods.subList(0, Math.min(10, foods.size()));
		Food[] result = new Food[foods.size()];
		foods.toArray(result);
		return result;
	}
}
